Demo · Forensic Pipeline topic
Walkthrough — Forensic Pipeline (Flow.fromFuture / deferFuture)
Demo: example\from_future_forensic_pipeline_demo.dart
One pipeline. Three layers in one job:
| Layer | Package idea | Job on this station |
|---|---|---|
| Cell | value + pulse + observe | What data is flowing; who may see it |
| Flow | deferFuture + asyncMap + synthesis | Bridge legacy APIs; preserve chain of custody |
| Observer | Cell.observe |
Log results and collect metrics |
Flow never modifies the source data. Cell never knows about legacy APIs. The observer is the only glue.
1. Why they have to combine
A legacy API call is a Future. Forensic metadata is provenance. Audit trails are I/O.
If you put API calls inside the observer, the observer becomes coupled to network latency. If you put audit logic inside the Future, the Future becomes impure. If you put forensic metadata in the Cell, the Cell becomes specific to one use case.
The demo's rule:
Flow answers: may this Future be bridged into the graph?
Cell answers: what is the current state of the evidence?
Observer answers: what should we record about the result?
2. Design (tagged)
Cell holds or carries. FlowInstruction is (pulse)→pulse|null.
Receptor runs the chain under one lock. Cell.observe is
(pulse)→side effect.
legacy API (Future-based)
│
▼
[Cell] ingress + TestCell FLOW intake
│ emit
▼
[Receptor] toHandle(source: cell) FLOW policy
│ [Instruction] deferFuture (await Future, emit result)
│ [Instruction] map (transform if needed)
│ null = stop; no side effects
▼
[Cell] rx.cell FLOW output
│ Cell.observe
▼
side effect (logging/collecting) glue (Dart)
| You need | You use |
|---|---|
| One-shot Future bridge | Flow fromFuture |
| Per-input Future bridge | Flow deferFuture |
| Multiple parallel Futures | Flow asyncMapConcurrent |
| Ordered Future steps | Flow deferFuture chain |
| Combine multiple sources | Flow synthesis |
| Error tracking | Flow onError callback |
| Provenance preservation | pulse.withStep() |
3. Step by step — Basic Future Bridge (scenario 1)
Read this as Cell then Flow then Observer.
0. Graph (once)
Flow.deferFuture with toHandle on Cell evidenceInput.
Cell.observe on evidenceHandle.cell → print results.
1. Pulse (Cell)
evidenceInput.emit('EV-12345') → Cell ingress.
2. Policy (Flow) deferFuture creates a new Future for each pulse:
| Stage | In | Out |
|---|---|---|
| deferFuture | 'EV-12345' |
Future<ForensicEvidence> |
| Future completes | — | ForensicEvidence emitted |
3. Observer (glue) Observer prints evidence details and trace.
4. Step by step — Chain of Custody (scenario 2)
Three sequential Future steps, then synthesis:
Input: 'EV-12345'
→ deferFuture (fetchEvidence) → ForensicEvidence
→ deferFuture (fetchImages) → List<ForensicImage>
→ deferFuture (fetchAuditLog) → List<ForensicAuditEntry>
→ synthesis → Map {evidence, images, audit}
Each step preserves provenance via withStep. Synthesis combines all three into one report.
Why this matters: The chain of custody is automatically preserved. Each API call adds a step to the pulse trace.
5. Step by step — Error Handling (scenario 3)
deferFuture with onError callback:
Input: 'EV-99999'
→ deferFuture (fetchEvidence)
→ EvidenceNotFoundException thrown
→ onError callback: prints error details
→ No output to observer
The error is captured with forensic context. The pipeline continues (no crash).
6. Step by step — Parallel Collection (scenario 4)
asyncMapConcurrent starts all three Futures at once:
Input: 1, 2, 3
→ asyncMapConcurrent
→ Source 1: fetchEvidence (500ms)
→ Source 2: fetchImages (350ms)
→ Source 3: fetchAuditLog (150ms)
→ Results emitted as they complete
Total time: ~350ms (not 500+350+150 = 1000ms).
Flow handles concurrency. Cell just carries the results.
7. Step by step — Sequential Processing (scenario 5)
Four deferFuture steps in sequence:
Input: 'forensic_user'
→ deferFuture (authenticate) → bool (200ms)
→ deferFuture (extract) → String (300ms)
→ deferFuture (analyze) → Map (200ms)
→ deferFuture (report) → String (150ms)
Total time: ~850ms. Each step waits for the previous to complete.
Flow handles ordering. Each step preserves provenance.
8. Step by step — Metadata Preservation (scenario 6)
deferFuture fetches multiple pieces of evidence:
Input: 'EV-12345'
→ fetchEvidence → ForensicEvidence
→ computeEvidenceHash → String
→ checkEvidenceStatus → String
→ Return: {evidence, hash, status, processingTime, sha256}
All metadata is preserved in the output. The pulse trace shows all steps.
9. Step by step — Legacy Integration (scenario 7)
deferFuture bridges a legacy system:
Input: 'LegacyDB'
→ fetchLegacyData → {system, records, version, lastSync}
→ Add forensic metadata
→ Return: {..., forensicTimestamp, forensicSource, forensicVersion}
Legacy data enters the reactive graph with full provenance.
10. Step by step — Audit Trail (scenario 8)
Complex pipeline with audit trail:
Input: {evidenceId: 'EV-12345', userId: 'forensic_user'}
→ createAuditEntry → ForensicAuditEntry
→ fetchEvidence → ForensicEvidence
→ computeEvidenceHash → String
→ Return: {
auditEntry, evidence, integrity, verified,
chainOfCustody: [
{step: 'AUTHENTICATION', status: 'PASSED'},
{step: 'EVIDENCE_RETRIEVAL', status: 'PASSED'},
{step: 'INTEGRITY_CHECK', status: 'PASSED'},
{step: 'AUDIT_CREATED', status: 'PASSED'},
]
}
Full audit trail with integrity verification. Chain of custody is explicit.
11. Flow.fromFuture vs Flow.deferFuture
| Feature | fromFuture | deferFuture |
|---|---|---|
| When to use | Single, one-shot Future | New Future per input |
| Trigger | First pulse only | Every pulse |
| Reusability | One-time | Reusable |
| Use case | Initialization, one-time load | Per-request, per-item |
fromFuture: Good for loading app config, user profile, initial data. deferFuture: Good for search queries, per-item loading, request-response.
12. Parts checklist
| Kind | Instances |
|---|---|
| Cell | evidenceInput, custodyInput, errorInput, parallelInput, processInput, metadataInput, legacyInput, auditInput |
| FlowInstruction | deferFuture, asyncMapConcurrent, synthesis |
| FlowHandle | one per pipeline |
| Observer | one per output cell |
| Not Cell | ForensicApi (legacy simulator), ForensicMetrics |
13. Rules for combining them
- Flow bridges Futures. Cell carries state. Observer logs.
- Use deferFuture for per-input async operations.
- Use fromFuture for one-shot async operations.
- Use asyncMapConcurrent for parallel operations.
- Chain deferFuture for sequential operations.
- Use synthesis to combine multiple sources.
- Handle errors in onError callback with forensic context.
- Preserve provenance with pulse.withStep().
Still demo-only
All APIs are simulated. Real-world APIs may have network latency, retries, and authentication. Forensic integrity checks are simplified.
Summary table
| Scenario | Flow does | Cell does | Observer does |
|---|---|---|---|
| 1 Basic | Bridges Future | Carries evidence | Prints result |
| 2 Chain | Chains 3 Futures + synthesis | Carries combined report | Prints chain length |
| 3 Error | Catches exception | No output | Prints error |
| 4 Parallel | Concurrent Futures | Carries results | Prints completion order |
| 5 Sequential | Sequential Futures | Carries report | Prints final report |
| 6 Metadata | Fetches multiple sources | Carries metadata | Prints integrity |
| 7 Legacy | Bridges legacy system | Carries migrated data | Prints provenance |
| 8 Audit | Full audit pipeline | Carries audit result | Prints chain of custody |
Classes
- Flow Features Architecture Walkthroughs Demo · ICU Alarm Pipeline Demo · Pharmacy Transactions Demo · Search Stability Demo · Forensic Pipeline HowTo-FlowInstructions Method Chaining (Fluent)
- The Transcription Orchestrator for the Cell framework, responsible for converting (transcribing) external stimuli, persistent state, and logic into reactive Cell topographies.